浅谈数据库XS锁实现事务 从而方便理解脏读 不可重复读 幻读
事务的最基本性质是ACID性质 关于这几个性质 我这里就不展开了
这是事务要实现的基础 这里具体解释的话内容太多 我也可能水平不够 不能够完整明确的讲解出来 但是需要记住事务实现的几个性质是ACID
分别是
- Atomicity 原子性
- Consistency 一致性
- Isolation 隔离性
- Durability 持久性
接下来直入主题 关于事务呢 就是实现并发下的不同客户的操作 也就是数据库的并发控制
并发会带来一些问题 在我个人的本科教科书上 是比常规的来说要多一个问题 这个问题在具体开发的时候并没有什么意义 这个属于最基本的保障 哪怕是最基本的数据库事务隔离级别也会实现它 这就是丢失更新问题 下面再详述 其他的三个就是属于本文上的 脏读 不可重复读 幻读
我们来一一说明下面为什么发生 具体在什么时候 什么原因下
下面的Find 就是为读数据 UPD就是更新数据 XFIND暂时理解为Find Rollback回滚 Commit提交
丢失更新问题
在7时间 丢失了事务T1的更新
读脏数据
读到了尚未提交的数据
一个是读了脏数据 但是没有破坏数据库完整性
另外一个就是读了脏数据 引起了数据丢失 破坏了数据库完整性
不可重复读
表示为T1需要俩次读取同一数据项 但是在俩次操作的间隔中 另一个事务T2改变了A的值
关于幻读
幻读的情况我暂且口述
以后有机会再重新画个图
这是在一个整体上的错误读写 事务T2读取到了这个表中有20行数据 并且针对这整个20行进行了修改数据
事务T1在这个过程中插入或删除行等方式来修改了这个表的20行数据
假设为插入 使其变为了21行
那么以后操作T2这个用户发现这个表中还有没有被影响到的行 就像产生了幻觉一样
这些问题都需要并发控制子系统来实现
那么就要说到本文的主题XS锁了
排他性封锁(X锁)写锁
定义为 如果事务T对某个数据R(可以是数据项 记录 数据集 乃至于整个数据库)实现了X锁 那么在T对数据R解除封锁之前 不允许其他事务T再对该数据加任何类型的锁 这个数据加了X锁之后只能被该事务进行读写
- 申请X锁的操作为XFIND R 表示事物对数据R申请加X锁 若成功 则可以读写数据R 若不成功,那么这个事务就会进入等待队列 一直到获准X锁 事务才能继续做下去
- 解除操作为 XRELEASE R 表示事务要解除对数据R的X锁
如果过早的解锁 有可能其他事务读了未提交数据(且随后被撤回) 引起丢失其他事务的更新 x锁的接触操作应该合并到事务的结束(ROLLBACK和COMMIT语句中包含了解除X锁的操作)
共享型封锁(S锁) 读锁
因为加X锁的只能被一个事务进行读写 所以为了并发的读 加入了S锁
定义为 如果事务T对数据加上S锁后 仍需要其他事务再对该数据加S锁
但在该数据的所有S锁都解除之前决不允许任何事务对该数据加X锁
使用S锁的操作有三个
- 申请S锁 SFIND R 表示事务对数据R申请加S锁
若成功则可以读数据R 但不可以写数据R - 升级和写操作 UPDX R 表示事务要吧数据R的S锁升级为X锁 若成功则更新数据R 否则这个事务进入等待队列
- 解除S锁操作 SRELEASE R
那我们就到了关键的部分了
这里提及一下关于封锁的粒度注意问题 封锁对象越大 并发度就越小 开销也就越小 相反的粒度越小 开销就大
在并发系统中是利用一个封锁协议来操作XS锁进行并发子系统控制的
这个封锁协议如下
级别 | 内 容 | 内容 | 内容 | 优点 | 缺点 |
---|---|---|---|---|---|
一级封锁协议 | 事务在修改数据之前,必须先对该数据加X锁直到事务结束时才释放 | 但只读数据可以不加锁 | 但只读数据可以不加锁 | 防止丢失修改 | 不加锁的数据 可能“读脏数据” 也可能“不可重复读” |
二级封锁协议 | 同上 | 但其他事务在读数据之前必须先加S锁 | 读完数据后即可释放S锁 | 防止“丢失修改” 防止“脏数据” | 对加S锁的事务 可能“不可重复读” |
三级封锁协议 | 同上 | 同上 | 直到事务结束时才释放S锁 | 防止“丢失修改”防止“读脏数据” 防止“不可重复读” | 无 |
那我们还是可能出现幻读 那幻读怎么实现避免的呢
这就是SERIALIZABLE 中文翻译过来叫串行化 这是多个事务依次执行的效果 如果利用分时的方法 同时处理多个事务 则称为事务的并行
可串行化概念
每个事务中 语句的先后顺序在各种调度中始终保持一致 在这个前提下 如果一个并发调度的执行结果和某一串行调度的结果等价 那么 这个调度就被称为“可串行化的调度” 否则就是不可串行化的调度
在这种情况下 我们可以保证每个事务的结果都是正确的 也自然防止了幻读
最后
事务的隔离级别
SQL提供事务的四种隔离级别让用户选择 这四个级别从高到低如下所述
- SERIALIZABLE(可串行化): 允许事务与其他事务并发执行 但系统必须保证并发调度是可串行化的 不致发生错误
- REPEATABLE READ(可重复读): 只允许事务读已提交的数据 并且在俩次读同一数据时不允许其他事务修改此数据
- READ COMMITTED(读提交数据):允许事务读已提交的数据 但不要求“可重复读” 例如 事务对同一记录的俩次读取之间 记录可能已被提交的事务更新
- READ UNCOMMITTED(可以读未提交数据):允许事务读已提交或未提交的数据 这是数据库允许的最低级别
参考于本科教材